package com.asen.commons.utils;

import com.asen.commons.config.RestTemplateConfig;
import com.asen.commons.constants.BaseConstant;
import com.asen.commons.context.ContextHolder;
import com.asen.commons.core.web.ErrorResponse;
import com.asen.commons.exception.RemoteException;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.MapUtils;
import org.springframework.http.HttpStatus;
import org.springframework.http.RequestEntity;
import org.springframework.http.ResponseEntity;
import org.springframework.web.client.RestClientResponseException;
import org.springframework.web.client.RestTemplate;

import java.net.URI;
import java.net.URLEncoder;
import java.util.List;
import java.util.Map;

/**
 * Rest工具类
 *
 * @author Gary
 * @version 1.0
 * @date 2019-06-12
 */
@Slf4j
public class RestUtils {

    private static final String NO_INSTANCES_AVAILABLE_FOR = "No instances available for";

    //public static <T> T get(Class<T> modelClazz, String path) throws Exception {
    //    return get("http://" + BillTypeUtils.getModuleName(modelClazz.getName()) + path, null, modelClazz);
    //}

    //public static <T> void post(Class<T> modelClazz, String path) throws Exception {
    //    post(modelClazz.getName(), path);
    //}

    //public static <T> void post(Class<T> modelClass, String path, Object data) throws Exception {
    //    post("http://" + BillTypeUtils.getModuleName(modelClass.getName()) + path, data, null);
    //}

    //public static <T, V> V post(Class<T> modelClass, String path, Object data, Class<V> retClazz) throws Exception {
    //    return post("http://" + BillTypeUtils.getModuleName(modelClass.getName()) + path, data, retClazz);
    //}

    /**
     * Get 请求
     *
     * @param serviceName
     * @param path
     * @throws Exception
     */
    public static void get(String serviceName, String path) throws Exception {
        get(serviceName, path, null, null);
    }

    /**
     * 服务间Rest调用，Get请求
     *
     * @param serviceName
     * @param path
     * @param clazz
     * @param <T>
     * @return
     * @throws Exception
     */
    public static <T> T get(String serviceName, String path, Class<T> clazz) throws Exception {
        return get(serviceName, path, null, clazz);
    }

    public static <T> T get(String serviceName, String path, Map<String, String> params) throws Exception {
        return get("http://" + serviceName + path, params, null);
    }

    public static <T> T get(String serviceName, String path, Map<String, String> params, Class<T> clazz) throws Exception {
        return get("http://" + serviceName + path, params, clazz);
    }

    public static <T> List<T> getForList(String serviceName, String path, Map<String, String> params, Class<T> clazz) throws Exception {
        String json = get("http://" + serviceName + path, params, String.class);
        return JsonMapper.json2list(json, clazz);
    }

    public static void post(String serviceName, String path) throws Exception {
        post(serviceName, path, null, null);
    }

    public static <T> T post(String serviceName, String path, Class<T> clazz) throws Exception {
        return post(serviceName, path, null, clazz);
    }

    public static void post(String serviceName, String path, Object data) throws Exception {
        post(serviceName, path, data, null);
    }

    public static <T> T post(String serviceName, String path, Object data, Class<T> clazz) throws Exception {
        return post("http://" + serviceName + path, data, clazz);
    }

    public static <T> List<T> postForList(String serviceName, String path, Object data, Class<T> clazz) throws Exception {
        String json = post("http://" + serviceName + path, data, String.class);
        return JsonMapper.json2list(json, clazz);
    }

    /**
     * 服务间Rest调用，Get请求
     *
     * @param url
     * @param clazz
     * @param <T>
     * @return
     * @throws Exception
     */
    public static <T> T get(String url, Class<T> clazz) throws Exception {
        return get(url, (Map<String, String>) null, clazz);
    }

    /**
     * 服务间Rest调用，Get请求
     *
     * @param url
     * @param params
     * @param resultClazz
     * @return
     * @throws Exception
     */
    public static <T> T get(String url, Map<String, String> params, Class<T> resultClazz) throws Exception {
        String args = new StringBuilder("GET请求，")
                .append("url：")
                .append(url)
                .append("，params:")
                .append(params)
                .append("，resultClazz：")
                .append(resultClazz).toString();

        log.debug(args);

        ResponseEntity<T> responseEntity = null;

        try {
            StringBuilder urlNames = new StringBuilder();
            urlNames.append(encodeUrl(url));
            if (MapUtils.isNotEmpty(params)) {
                boolean hasParams = url.contains("?");
                for (Map.Entry<String, String> entry : params.entrySet()) {
                    if (!hasParams) {
                        urlNames.append("?");
                        hasParams = true;
                    } else {
                        urlNames.append("&");
                    }
                    urlNames.append(entry.getKey());
                    urlNames.append("=");
                    urlNames.append(URLEncoder.encode(entry.getValue(), "UTF-8"));
                }
            }

            //String token = ContextHolder.getAuthToken();
            //String stringTenantId = ContextHolder.getStringTenantId();

            RequestEntity.HeadersBuilder headersBuilder = RequestEntity.get(new URI(urlNames.toString()));
            headersBuilder.header(BaseConstant.FEIGN_SERVER_REQUEST_KEY, "true");
            //if (token != null) {
            //    headersBuilder.header(BaseConstant.AUTH_HEADER, "Bearer " + token);
            //}
            //if (stringTenantId != null) {
            //    headersBuilder.header(BaseConstant.TENANT_ID, stringTenantId);
            //}
            RequestEntity requestEntity = headersBuilder.build();

            RestTemplate restTemplate = (RestTemplate) SpringUtil.getBean(RestTemplateConfig.COMMON_REST_TEMPLATE);
            responseEntity = restTemplate.exchange(requestEntity, resultClazz);
        } catch (Exception e) {
            //log.error("请求失败！" ,e);
            handleException("请求失败！" + args, e);
        }

        return handleResponse(responseEntity, args);
    }

    /**
     * 服务间Rest调用，Post请求
     *
     * @param url
     * @param data
     * @param resultClazz
     * @param <T>
     * @return
     * @throws Exception
     */
    public static <T> T post(String url, Object data, Class<T> resultClazz) throws Exception {
        String args = null;
        if (StringUtils.endsWithAny(url, "/add", "/mod", "/update")) {
            args = new StringBuilder("POST请求，")
                    .append("url：")
                    .append(url)
                    .append("，data: ***")
                    .append("，resultClazz：")
                    .append(resultClazz).toString();
        } else {
            args = new StringBuilder("POST请求，")
                    .append("url：")
                    .append(url)
                    .append("，data:")
                    .append(data)
                    .append("，resultClazz：")
                    .append(resultClazz).toString();
        }

        log.debug(args);

        ResponseEntity<T> responseEntity = null;

        try {
            String token = ContextHolder.getAuthToken();
            String stringTenantId = ContextHolder.getStringTenantId();

            RequestEntity.BodyBuilder bodyBuilder = RequestEntity.post(new URI(encodeUrl(url)));
            bodyBuilder.header(BaseConstant.FEIGN_SERVER_REQUEST_KEY, "true");
            bodyBuilder.header("Content-Type", "application/json");
            if (token != null) {
                bodyBuilder.header(BaseConstant.AUTH_HEADER, "Bearer " + token);
            }
            if (stringTenantId != null) {
                bodyBuilder.header(BaseConstant.TENANT_ID, stringTenantId);
            }
            RequestEntity requestEntity = bodyBuilder.body(data);

            RestTemplate restTemplate = (RestTemplate) SpringUtil.getBean(RestTemplateConfig.COMMON_REST_TEMPLATE);
            responseEntity = restTemplate.exchange(requestEntity, resultClazz);
        } catch (Exception e) {
            handleException("请求失败！" + args, e);
        }

        return handleResponse(responseEntity, args);
    }

    private static String encodeUrl(String url) throws Exception {
        StringBuilder result = new StringBuilder();

        int index = url.indexOf("?");
        if (index > -1) {
            result.append(url.substring(0, index));
            String paramsUrl = url.substring(index + 1);
            int i = 0;
            for (String param : paramsUrl.split("&")) {
                if (i == 0) {
                    result.append("?");
                } else {
                    result.append("&");
                }
                String[] keyVal = param.split("=");
                result.append(keyVal[0]);
                if (keyVal.length > 1) {
                    result.append("=");
                    result.append(URLEncoder.encode(keyVal[1], "UTF-8"));
                }
                i++;
            }
        } else {
            result.append(url);
        }

        return result.toString();
    }

    private static void handleException(String msg, Exception e) throws Exception {
        if (e instanceof RestClientResponseException) {
            ErrorResponse errorResponse = null;
            String responseBody = ((RestClientResponseException) e).getResponseBodyAsString();

            try {
                errorResponse = JsonMapper.json2obj(responseBody, ErrorResponse.class);
            } catch (Exception e1) {
                log.error("远程服务响应消息解析失败！msg=" + msg + ", response=" + responseBody, e1);
                throw new RemoteException("系统正在维护中，请稍后！", "消息=" + msg + ", 响应=" + responseBody);
            }

            if (errorResponse != null) {
                throw errorResponse.toException();
            } else {
                log.error(msg + "响应消息response为空！", e);
                throw new RemoteException("系统正在维护中，请稍后！", "消息=" + msg + ", 响应=" + responseBody);
            }
        } else if (e instanceof IllegalStateException) {
            log.error(msg, e);

            if (e.getMessage() == null) {
                throw new RemoteException("系统正在维护中，请稍后！", msg);
            }

            //int index = e.getMessage() == null ? -1 : e.getMessage().indexOf(NO_INSTANCES_AVAILABLE_FOR);
            //if (index > -1) {
            //    String serviceMessage = e.getMessage().substring(NO_INSTANCES_AVAILABLE_FOR.length()).trim();
            //    ServiceNameEnum serviceName = EnumUtils.valueOf(ServiceNameEnum.class, serviceMessage);
            //    if (serviceName != null) {
            //        throw new RemoteException("系统【" + serviceName.getDesc() + "】正在维护中，请稍后！", msg);
            //    } else {
            //        throw new RemoteException("系统【" + serviceMessage + "】正在维护中，请稍后！", msg);
            //    }
            //} else {
                throw new RemoteException("系统正在维护中，请稍后！", msg);
            //}
        } else {
            log.error(msg, e);
            throw new RemoteException("系统正在维护中，请稍后！", msg);
        }
    }

    private static <T> T handleResponse(ResponseEntity<T> responseEntity, String args) {
        if (responseEntity == null) {
            log.error("请求失败，无响应消息！{}", args);
            throw new RemoteException("系统正在维护中，请稍后！", "内部服务调用失败，无响应消息！" + args);
        }

        HttpStatus httpStatus = responseEntity.getStatusCode();
        if (!HttpStatus.OK.equals(httpStatus)) {
            log.error("请求失败！statusCode：{}, errorMessage: {}, {}", httpStatus.value(), httpStatus.getReasonPhrase(), args);
            throw new RemoteException("系统正在维护中，请稍后！", "内部服务调用失败！错误码：" + httpStatus.value() + "，错误消息：" + httpStatus.getReasonPhrase() + args);
        }

        return responseEntity.getBody();
    }
}
